package validator

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/log"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
)

/// solution one interface
func ValidateData(data *mpcprotocol.SendData) (bool, error) {

	log.SyslogInfo("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ValidateData, begin",
		"pk", hexutil.Encode(data.PKBytes),
		"data", hexutil.Encode([]byte(data.Data)),
		"curveType", hexutil.Encode(data.Curve))

	sdb, err := GetDB()
	if err != nil {
		log.SyslogErr("GetDataForApprove, getting storeman database fail", "err", err.Error())
		return false, mpcprotocol.ErrGetDb
	}

	approvedKey := buildKeyFromData(data, mpcprotocol.MpcApproved)
	_, err = waitKeyFromDB([][]byte{approvedKey})
	if err != nil {
		log.SyslogErr("ValidateData, waitKeyFromDB has fail", "err", err.Error())
		return false, mpcprotocol.ErrWaitApproved
	}

	value, err := sdb.Get(approvedKey)
	if err != nil {
		log.SyslogErr("ValidateData, sdb.Get has fail", "err", err.Error())
		return false, mpcprotocol.ErrGetApproved
	}

	var byteRev []byte
	byteRev, err = json.Marshal(&data)
	if err != nil {
		log.SyslogErr("ValidateData, check has fail", "err", err.Error())
		return false, mpcprotocol.ErrMarshal
	}

	if !bytes.Equal(value, byteRev) {
		var dataInDb mpcprotocol.SendData
		var dataRcv mpcprotocol.SendData

		err := json.Unmarshal(value, &dataInDb)
		if err != nil {
			log.SyslogErr("ValidateData, Unmarshal dataInDb err", err.Error())
		}

		err = json.Unmarshal(byteRev, &dataRcv)

		inDbStr := fmt.Sprintf("%+v", dataInDb)
		rcvStr := fmt.Sprintf("%+v", dataRcv)

		if err != nil {
			log.SyslogErr("ValidateData, Unmarshal dataRcv err", err.Error())
		}

		log.SyslogErr("ValidateData, bytes not equal", "data in db", inDbStr, "data recv", rcvStr)
		return false, mpcprotocol.ErrApprovedNotConsistent
	}
	log.SyslogInfo("ValidateData successfully", "data", data.String())
	return true, nil

}

func AddValidData(data *mpcprotocol.SendData) error {
	log.SyslogInfo("AddValidData", "data", data.String())
	return addOneValidData(*data)
}

/// solution two interface
func AddApprovingData(data *mpcprotocol.SendData) error {
	log.SyslogInfo("AddApprovingData", "data", data.String())
	return addApprovingData(data)
}

func GetDataForApprove() ([]mpcprotocol.SendData, error) {
	log.SyslogInfo("GetDataForApprove, begin")

	approvingBytes, _ := getForApprove([]byte(mpcprotocol.MpcApprovingKeys))

	var approvingData []mpcprotocol.SendData
	for i := 0; i < len(approvingBytes); i++ {

		var approvingTemp mpcprotocol.SendData
		err := json.Unmarshal(approvingBytes[i], &approvingTemp)
		if err != nil {
			log.SyslogErr("GetDataForApprove, Unmarshal approvingTemp", "err", err.Error())
			//return nil, err
			continue
		}
		approvingData = append(approvingData, approvingTemp)
	}
	return approvingData, nil
}

func ApproveData(approveData []mpcprotocol.SendData) []error {
	retResult := make([]error, len(approveData))
	for i := 0; i < len(approveData); i++ {
		dataItem := approveData[i]
		log.SyslogInfo("ApproveData", "data", dataItem.String())
		retResult[i] = approveOneData(dataItem)
	}

	return retResult
}

/// internal function for schnorr

func approveOneData(dataItem mpcprotocol.SendData) error {
	approvedKey := buildKeyFromData(&dataItem, mpcprotocol.MpcApproved)
	approvingKey := buildKeyFromData(&dataItem, mpcprotocol.MpcApproving)
	approvingKeysKey := []byte(mpcprotocol.MpcApprovingKeys)
	value, err := json.Marshal(&dataItem)
	if err != nil {
		log.SyslogErr("approveOneData, json.Marshal fail", "err", err.Error())
		return err
	}

	return approve(approvedKey, approvingKey, approvingKeysKey, value)
}

func addOneValidData(approveData mpcprotocol.SendData) error {

	sdb, err := GetDB()
	if err != nil {
		log.SyslogErr("addOneValidData, getting storeman database fail", "err", err.Error())
		return err
	}

	key := buildKeyFromData(&approveData, mpcprotocol.MpcApproved)
	exist, err := sdb.Has(key)
	if exist {
		log.SyslogErr("addOneValidData, has in approved db")
		return errors.New("has in approved db")
	}
	if err != nil {
		log.SyslogErr("addOneValidData, sdb.Has error", "err:", err.Error())
		return err
	}
	// in approving db
	// add in approved DB
	//log.SyslogInfo("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& addOneValidData, begin",
	//	"pk", hexutil.Encode(approveData.PKBytes),
	//	"data", hexutil.Encode([]byte(approveData.Data)),
	//	"Extern", hexutil.Encode([]byte(approveData.Extern)))

	log.SyslogInfo("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& addOneValidData, begin", approveData.String())

	val, err := json.Marshal(&approveData)
	if err != nil {
		log.SyslogErr("addOneValidData, marshal fail", "err", err.Error())
		return err
	}

	log.SyslogInfo("=============== approveOneData", "data", approveData.String(), "approved key", hexutil.Encode(key))
	err = addKeyValueToDB(key, val)
	if err != nil {
		log.SyslogErr("addOneValidData, addKeyValueToDB fail", "err", err.Error())
		return err
	}
	return nil
}

func buildKeyBytesFromData(data *mpcprotocol.SendData) []byte {
	// build the key.
	var buffer bytes.Buffer
	buffer.Write(data.PKBytes[:])
	buffer.Write([]byte(data.Data[:]))
	buffer.Write(data.Curve)
	return buffer.Bytes()
}

// status: approving || approved
func buildKeyFromData(data *mpcprotocol.SendData, status string) []byte {
	// build the key.
	keyBytes := buildKeyBytesFromData(data)
	return buildKeyFromBytes(keyBytes, status)
}

func addApprovingData(dataItem *mpcprotocol.SendData) error {

	approvedKey := buildKeyFromData(dataItem, mpcprotocol.MpcApproved)
	approvingKey := buildKeyFromData(dataItem, mpcprotocol.MpcApproving)
	approvingKeysKey := []byte(mpcprotocol.MpcApprovingKeys)
	value, err := json.Marshal(&dataItem)
	if err != nil {
		log.SyslogErr("addApprovingData, json.Marshal fail", "err", err.Error())
		return err
	}

	return addApproving(approvedKey, approvingKey, approvingKeysKey, value)
}
